package cn.seaboot.flake.reverse;

import cn.seaboot.flake.entity.NormColumn;
import cn.seaboot.flake.entity.NormTable;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.create.table.ColDataType;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.create.table.CreateTable;

import java.math.BigInteger;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 建表语句解析器
 *
 * 从建表语句逆向分析表结构，方便程序进一步加工，预测未来可能的需要的代码
 */
public class CreateTableParser {


    /**
     * 从 sql 语句中分析出表结构信息
     *
     * @param sql 表创建语句
     * @return -
     * @throws JSQLParserException -
     */
    public NormTable parse(String sql) throws JSQLParserException {
        // 解析SQL语句
        Statement statement = CCJSqlParserUtil.parse(sql);

        NormTable normTable = new NormTable();

        // 检查是否是CREATE TABLE语句
        if (statement instanceof CreateTable) {
            CreateTable createTable = (CreateTable) statement;

            // schema name alias
            Table table = createTable.getTable();
            normTable.setTableName(name(table.getName()));
            normTable.setSchemaName(table.getSchemaName());

            // table-comment
            List<String> options = createTable.getTableOptionsStrings();
            int idx = options.indexOf("COMMENT");
            if (idx > 0 && options.size() > idx + 2) {
                normTable.setTableComment(options.get(idx + 2));
            }

            // column definition
            List<NormColumn> ret = createTable.getColumnDefinitions().stream().map(definition -> {
                NormColumn normColumn = new NormColumn();

                normColumn.setColumnName(name(definition.getColumnName()));

                parserType(normColumn, definition);
                parseNullable(normColumn, definition);
                parseComment(normColumn, definition);
                return normColumn;
            }).collect(Collectors.toList());
            normTable.setColumns(ret);
        } else {
            throw new JSQLParserException(statement.toString());
        }
        return normTable;
    }

    public String name(String name){
        return name.replace("'", "").replace("`", "");
    }

    /**
     * 解析字段类型
     *
     * @param normColumn -
     * @param definition -
     */
    public void parserType(NormColumn normColumn, ColumnDefinition definition) {
        ColDataType dataType = definition.getColDataType();
        switch (dataType.getDataType()) {
            case "varchar":
            case "char":
            case "text":
            case "longtext":
            case "varchar2":
            case "nvarchar":
                parserString(normColumn, dataType);
                break;
            case "tinyint":
                parserBoolean(normColumn, dataType);
                break;
            case "int":
            case "bigint":
            case "integer":
            case "smallint":
            case "real":
            case "float":
            case "double":
            case "number":
            case "numeric":
                parserNumber(normColumn, dataType);
                break;
            case "datetime":
                parserDatetime(normColumn, dataType);
                break;
            default:
                throw new IllegalArgumentException("unknown dataType: " + dataType);
        }
    }

    /**
     * 解析布尔类型的字段
     *
     * @param normColumn -
     * @param dataType   -
     */
    public void parserBoolean(NormColumn normColumn, ColDataType dataType) {
        List<String> args = dataType.getArgumentsStringList();

        normColumn.setNumericScale(0);
        normColumn.setNumericPrecision(1);

        // type
        normColumn.setSuperType("boolean");
        normColumn.setColumnType(dataType.toString());
        normColumn.setJdbcType(dataType.getDataType());
    }


    /**
     * 解析数字类型的字段
     *
     * @param normColumn -
     * @param dataType   -
     */
    public void parserNumber(NormColumn normColumn, ColDataType dataType) {
        List<String> args = dataType.getArgumentsStringList();

        // type
        normColumn.setSuperType("number");
        normColumn.setColumnType(dataType.toString());
        normColumn.setJdbcType(dataType.getDataType());

        // number limit
        if (args != null && !args.isEmpty()) {
            if (args.size() == 1) {
                normColumn.setNumericPrecision(Integer.parseInt(args.get(0)));
                normColumn.setNumericScale(0);
            } else {
                normColumn.setNumericPrecision(Integer.parseInt(args.get(0)));
                normColumn.setNumericScale(Integer.parseInt(args.get(1)));
            }

            // unsigned number type
            if (args.contains("unsigned")) {
                normColumn.setColumnType(normColumn.getColumnType() + " unsigned");
            }
        }
    }


    /**
     * 解析字符串类型的字段
     *
     * @param normColumn -
     * @param dataType   -
     */
    public void parserString(NormColumn normColumn, ColDataType dataType) {
        List<String> args = dataType.getArgumentsStringList();
        String type = dataType.getDataType();

        // string length limit
        if ("text".equals(type)) {
            normColumn.setLength(BigInteger.valueOf(65535L));
        } else if ("longtext".equals(type)) {
            normColumn.setLength(BigInteger.valueOf(4294967295L));
        } else if (args != null && !args.isEmpty()) {
            long length = Long.parseLong(args.get(0));
            normColumn.setLength(BigInteger.valueOf(length));
        }

        // type
        normColumn.setSuperType("string");
        // column-type
        String columnType = dataType.toString();
        int idx = columnType.indexOf("CHARACTER");
        if(idx > 0){
            columnType = columnType.substring(0, idx - 1);
        }
        normColumn.setColumnType(columnType);
        normColumn.setJdbcType(dataType.getDataType());
    }


    /**
     * 解析数字类型的字段
     *
     * @param normColumn -
     * @param dataType   -
     */
    public void parserDatetime(NormColumn normColumn, ColDataType dataType) {
        // type
        normColumn.setSuperType("datetime");
        normColumn.setColumnType(dataType.toString());
        normColumn.setJdbcType(dataType.getDataType());
    }

    /**
     * 解析字段是否允许为空
     *
     * @param normColumn -
     * @param definition -
     */
    public void parseNullable(NormColumn normColumn, ColumnDefinition definition) {
        List<String> args = definition.getColumnSpecs();

        // default value
        int idx = args.indexOf("DEFAULT");
        if (idx > 0 && args.size() > idx + 1) {
            String v = args.get(idx + 1);
            if (!"NULL".equals(v)) {
                normColumn.setDefaultValue(v);
            }
        }

        // nullable
        idx = args.indexOf("NULL");
        if (idx > 0) {
            normColumn.setNullable(!"NOT".equals(args.get(idx - 1)));
        }

        // require
        normColumn.setRequire(Boolean.FALSE.equals(normColumn.getNullable()) && normColumn.getDefaultValue() == null);
    }

    /**
     * 解析字段注释
     *
     * @param normColumn -
     * @param definition -
     */
    public void parseComment(NormColumn normColumn, ColumnDefinition definition) {
        List<String> args = definition.getColumnSpecs();
        int idx = args.indexOf("COMMENT");
        if (args.size() > idx + 1) {
            normColumn.setColumnComment(args.get(idx + 1).replace("'", ""));
        }
    }
}